package mspbots.teams.client.core.teams;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.teams.client.core.AbstractToolsUtil;
import mspbots.teams.client.core.BaseSyncService;
import mspbots.teams.client.core.SyncRequest;
import mspbots.teams.client.core.data.apps.AppsServiceImpl;
import mspbots.teams.client.oauth.TokenState;
import mspbots.teams.common.RedisKeys;
import mspbots.teams.common.ResultSync;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Map;

/**
 * TeamsService
 *
 * @author <a href="https://github.com/vnobo">Alex bob</a>
 * @date Created by 2020/8/28
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class TeamsServiceImpl extends AbstractToolsUtil {

    private final Map<String, BaseSyncService> syncServiceMap;

    private final AppsServiceImpl teamsAppService;

    public Mono<Boolean> onOff(Long tenantId, String onOff) {
        if ("ON".equals(onOff.toUpperCase())) {
            return this.stringRedisTemplate.opsForValue()
                    .set(RedisKeys.ON_OFF_TENANT_TASK_REDIS_KEY + tenantId, onOff.toUpperCase(),
                            Duration.ofDays(365));
        } else {
            return this.stringRedisTemplate.opsForValue()
                    .delete(RedisKeys.ON_OFF_TENANT_TASK_REDIS_KEY + tenantId);
        }
    }

    public Mono<JsonNode> initialize(Long tenantId) {
        ResultSync resultSync = ResultSync.withDefault("TEST-SECURITY");
        TokenState tokenState = TokenState.withDefault(tenantId);
        return super.teamsClient.get("/v1.0/me", Map.of())
                .subscriberContext(ctx -> ctx.put("tokenState", tokenState))
                .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                .doOnSuccess(result -> Flux.fromStream(this.syncServiceMap.values().parallelStream())
                        .flatMap(v -> Flux.concatDelayError(v.syncFast(tokenState), v.syncSlow(tokenState))).subscribe());
    }

    public Mono<Boolean> deleting(Long tenantId) {
        return this.stringRedisTemplate.opsForValue()
                .delete(RedisKeys.OAUTH_TENANT_REDIS_KEY_PREFIX + tenantId);
    }

    public Mono<ObjectNode> synchronise(Long tenantId, SyncRequest syncRequest) {
        return Mono.just(this.objectMapper.createObjectNode().put("result", "success"))
                .doOnSuccess(res -> this.stringRedisTemplate.opsForValue().
                        delete(RedisKeys.USERS_ANCHOR_REDIS_KEY_PREFIX + tenantId)
                        .flatMapMany(r -> Flux.fromStream(this.syncServiceMap.values().parallelStream()))
                        .flatMap(v -> Flux.concatDelayError(v.syncFast(TokenState.withDefault(tenantId)),
                                v.syncSlow(TokenState.withDefault(tenantId))))
                        .subscribe(result -> log.info("Synchronise manual sync success."),
                                err -> log.error("Synchronise manual sync error, msg: {}", err.getMessage())));
    }

    public Flux<JsonNode> existsApplication(TokenState tokenState) {
        return this.teamsAppService.find(tokenState);
    }

    public Mono<String> loginSync(TokenState state) {
        return Mono.just("Sync login users success.")
                .doOnSuccess(res -> this.syncServiceMap.get("usersServiceImpl")
                        .syncFast(state)
                        .subscribe());
    }
}
